# 模拟SP2仪器
# 参考数据 raw_data\课题3\SP2\SP2_20180627100837.hk
# 自动变化数据：
# Incand. Conc:黑碳数浓度(#/cc); Scatter. Conc:非黑碳数浓度(#/cc)

import parse
import fire
import time
import datetime
import os
import random
import regex as re
from math import sin

# 输出log，便于调试
import logging


def update_data_dict_gen(t=None,mode='sin',selected_cols=None,data_types=None):
    """假造一些数据，返回字典格式
    """
    now_ts = time.time() if t is None else t # 默认当前时间
    update_data_dict = {}
    
    # 时间处理
    now_datetime = datetime.datetime.fromtimestamp(now_ts)
    # time in seconds since the start of the day
    update_data_dict['time'] = (now_datetime - datetime.datetime(year=now_datetime.year,month=now_datetime.month,day=now_datetime.day)).total_seconds()


    # 生成数据
    if mode == 'sin':
        # 正弦变化的数据
        period = 100 # s
        shift = 10 # s
        for i,col in enumerate(selected_cols):
            if data_types[col].lower() in 'feg':
                update_data_dict[col] = 1000*(2+sin((now_ts+shift*i)/period))
            elif data_types[col].lower() in 'd':
                update_data_dict[col] = int(1000*(1+sin((now_ts+shift*i)/period)))
            else :
                raise RuntimeError(f'unknown data type {col}:{data_types[col]}')
    elif mode == 'random':
        # 随机数据
        max = 1.0
        min = 0.0
        for i,col in enumerate(selected_cols):
            update_data_dict[col] = random.random()*(max-min)+min
    else:
        raise RuntimeError(f'mode={mode}, it must be "sin" or "random"')
    return update_data_dict

def main(output_filename= None,
        delay_time = 0.95,
        test_mode=False,
        max_time_hours = 24,
        mode='sin',
        log_level = 'INFO'):
    """假造一些输出到log文件，模拟实际设备
    """
    # 初始化
    logging.basicConfig(level = getattr(logging,log_level.upper(),logging.INFO)) # 默认info
    file_base_name = os.path.basename(__file__)
    logger = logging.getLogger(file_base_name)

    # 分隔符
    sep = '\t'
    # 列名
    ## 33个
    header = 'Time (sec)	Sample Flow Set (vccm)	Sample Flow LFE (vccm)	YAG Power (V)	Incand. Conc. (#/cc)	Sheath Flow Set (vccm)	Sheath Flow Read (vccm)	YAG Crystal Temp (C)	YAG Heat Sink Temp (C)	Chamber Temp (C)	Chamber Pressure (mBar)	Pump Diode Power (V)	Duty Cycle (%)	Timestamp	Elapsed Time	Error Code	Electronics Temp (C)	Aux Input 0 (V)	Sheath Flow Read (sccm)	Purge Flow Read (sccm)	Chan 0 HV (V)	Aux Input 1 (V)	Aux Input 2 (V)	Chan 3 HV (V)	Purge Flow Read (vccm)	Purge Flow Set (vccm)	Exhaust Valve Set (Volts)	Sheath Flow Set (sccm)	Scatter. Conc. (#/cc)	Primary Threshold	Secondary Threshold	Num of Buffers	Num of Particles	Num Written to File	Num in File	Laser Current (mA)	Laser Curr Setpoint (mA)	Laser Voltage (V)	Laser Temp (C)	Laser TEC Temp (C)	Laser TEC Temp Setpoint	Laser TEC Current (mA)'
    display_names = header.split('\t')
    col_names = 'time,sample_flow_set_vccm,sample_flow_LFE_vccm,yag_power,incand_conc,sheath_flow_set_vccm,sheath_flow_read_vccm,yag_crystal_temp,yag_heat_sink_temp,chamber_temp,chamber_pressure,pump_diode_power,duty_cycle,timestamp,elapsed_time,error_code,electronics_temp,aux_input_0,sheath_flow_read_sccm,purge_flow_read_sccm,chan_0_hv,aux_input_1,aux_input_2,chan_3_hv,purge_flow_read_vccm,purge_flow_set_vccm,exhaust_valve_set,sheath_flow_set_sccm,scatter_conc,primary_threshold,secondary_threshold,num_of_buffers,num_of_particles,num_written_to_file,num_in_file,laser_current,laser_curr_setpoint,laser_voltage,laser_temp,laser_TEC_temp,laser_TEC_temp_set_point,laser_TEC_current'.split(',')

    # 默认值，为保持完整性
    defaults_str = '36576.750	120.000	117.593	3.960	506.678	1000.000	1002.733	56.672	32.708	32.515	931.173	1.809	100.000	3.61291E+9	61.4375	0	37.529923	0.002441	823.486328	252.075195	278.930664	0.002441	0.003052	376.327515	306.943897	246.746136	2.154847	822.487121	159.426854	-26668	-29394	5	1337	1337	42403	1948	1950	1.84	37.84	32.119999	32	531'
    
    data_write_formats = ['.3f']*13+['.5e','.4f','d']+['.6f']*13+['d']*8+['.2f']*2+['.6f']+['d']*2
    data_read_formats = [format[-1] for format in data_write_formats]

    # data types
    data_types = dict(zip(col_names, [format[-1] for format in data_write_formats]))

    # selected cols
    selected_cols = [col.strip() for col in 'incand_conc,scatter_conc'.split(',') ]

    # 生成格式化字符串
    line_write_formatter=sep.join([f'{{{col_name}:{format}}}' for col_name,format in zip(col_names,data_write_formats)])
    logger.info(line_write_formatter)

    line_read_formatter=sep.join([f'{{{col_name}:{format}}}' for col_name,format in zip(col_names,data_write_formats)])
    logger.info(line_read_formatter)

    # 简单测试
    parse_result = parse.parse(line_read_formatter,defaults_str)
    logger.info(parse_result)
    if parse_result is None:
        raise RuntimeError('Something go wrong!')

    # 默认值
    data_dict_defaults = parse_result.named
    
    # 输出文件名
    output_filename = output_filename if output_filename is not None else (file_base_name+r".test.log")
    with open(output_filename,'w+',encoding='utf-8') as OF:
        if test_mode: OF = None
        #no header
        #data lines
        start_time = time.time()
        while(time.time()<start_time + max_time_hours*24*3600): # max hours
            data_dict = data_dict_defaults
            update_data_dict = update_data_dict_gen(mode=mode,selected_cols=selected_cols,data_types=data_types)
            logger.info(update_data_dict)
            data_dict.update(update_data_dict)
            line = line_write_formatter.format(**data_dict)
            print(line,file=OF,flush=True)# flush is necessary
            time.sleep(delay_time)

if __name__ == "__main__":
    fire.Fire(main) # 利用fire命令行化